import {Component, OnInit, ViewChild, EventEmitter, OnDestroy, ElementRef, ApplicationRef, Output, Input} from '@angular/core';
import {NzFormatEmitEvent, NzTreeComponent, NzTreeNode, NzTreeNodeOptions} from 'ng-zorro-antd/tree';
import {NzModalRef, NzModalService} from 'ng-zorro-antd/modal';
import {PropsConfigComponent} from './props-config/props-config.component';
import {MessageService} from '@shared-services/message';
import {environment} from '@environments';
import {Auth} from '@shared-services/auth';
import {HttpClient} from '@angular/common/http';
import {MqttService} from 'ngx-mqtt';
import {fromEvent, Subscription} from 'rxjs';
import {UtilMisc} from '@shared-utils/misc';
import {NzContextMenuService, NzDropdownMenuComponent} from 'ng-zorro-antd/dropdown';
import {ResultConfirmComponent} from './result-confirm/result-confirm.component';
import {map} from 'rxjs/operators';

type TestStatus = 'running' | 'suspend' | 'stop' | 'finished' | null;
const TestStatusMap = {running: '进行中', suspend: '暂停中', stop: '已终止', finished: '已完成'};
type TestItem = { key: string, progress: number, result?: { passed: 'Y' | 'N' } };

@Component({
  selector: 'app-main',
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.scss']
})
export class MainComponent implements OnInit, OnDestroy {


  @Output()
  deviceChangeEvent = new EventEmitter();
  @Output()
  reportChangeEvent = new EventEmitter();
  @Input()
  temperatureAvgData: any;
  testStatus: TestStatus = null;
  @ViewChild('nzTreeComponent', {static: false}) nzTreeComponent!: NzTreeComponent;
  @ViewChild('logContent') logContent: ElementRef;
  defaultCheckedKeys = [];
  defaultSelectedKeys = [];
  defaultExpandedKeys = ['interfaceTest', 'statusTest', 'performanceTest'];
  modalRef: NzModalRef;
  nodes: NzTreeNodeOptions[] = [
    {
      title: '设备接口测试单元', key: 'interfaceTest',
      children: [
        {
          title: 'DVI接口测试', key: 'dviTest', isLeaf: true, manual: true,
          testMethod: '将测试台的DVI接口与被测设备的DVI接口相连，由测试人员观察测试台显示器是否正常显示。'
        },
        {
          title: 'VGA接口测试', key: 'vgaTest', isLeaf: true, manual: true,
          testMethod: '将测试台的VGA接口与被测设备的VGA接口相连，由测试人员观察测试台显示器是否正常显示。'

        },
        // {title: '视频接口切换测试', key: 'videoPortSwitchTest', isLeaf: true, manual: true},
        {
          title: 'PAL接口测试', key: 'palTest', isLeaf: true, manual: true,
          testMethod: '将测试台的PAL接口与被测设备的PAL接口相连，由测试人员观察测试台显示器是否正常显示。'

        },
        {title: 'RS422接口测试', key: 'RS422Test', isLeaf: true, timeCost: 10000},
        {title: 'RS232接口测试', key: 'RS232Test', isLeaf: true, timeCost: 10000},
        {
          title: '音频输入/出测试', key: 'audioTest', isLeaf: true, manual: true, needTestAgent: true,
          testMethod: '将测试台的附带的耳麦与被测设备连接，点击开始录音，系统录制10秒音频，随后播放该录音，由测试人员判断被测设备音频输入输出是否正常。'
        },
        {title: 'USB传输测试', key: 'usbTest', isLeaf: true, timeCost: 10000},
        {
          title: 'PS/2接口传输测试', key: 'ps2Test', isLeaf: true, manual: true,
          testMethod: '将测试台的PS/2接口与被测设备的PS/2接口相连，由测试人员操作与之对应的鼠标、键盘，并观察被测设备是否正常响应相关动作。'
        },
        {title: 'IP配置', key: 'ipConfig', isLeaf: true, timeCost: 5000},
        {title: '网口测试', key: 'networkTest', isLeaf: true, timeCost: 15000}
      ]
    },
    {
      title: '设备状态测试单元', key: 'statusTest',
      children: [
        // {title: '配置读取', key: 'propRead', isLeaf: true, timeCost: 3000},
        // {title: '状态监控', key: 'statusMonitoring', isLeaf: true},
        {
          title: '鼠标适配测试', key: 'mouseTest', isLeaf: true, manual: true, needTestAgent: true,
          testMethod: '将测试台的鼠标与被测设备相连，由测试人员操作鼠标，并观察被测设备是否正常响应相关动作。'
        },
        {
          title: '键盘适配测试', key: 'keyBoardTest', isLeaf: true, manual: true, needTestAgent: true,
          testMethod: '将测试台的键盘与被测设备相连，由测试人员操作键盘，并观察被测设备是否正常响应相关动作。'
        },
        // {
        //   title: '触屏适配测试', key: 'touchTest', isLeaf: true, manual: true,
        //   testMethod: '将测试台的触摸屏与被测设备相连，由测试人员操作触摸屏，并观察被测设备是否正常响应相关动作。'
        // },
        // {title: '异常告警', key: 'warning', isLeaf: true}
      ]
    },
    {
      title: '设备性能测试单元', key: 'performanceTest',
      children: [
        {title: '算数性能测试', key: 'cpuTest', isLeaf: true, timeCost: 90000},
        {title: '图像性能测试', key: 'gpuTest', isLeaf: true, timeCost: 30000},
        {title: '读写性能测试', key: 'diskTest', isLeaf: true, timeCost: 20000}
      ]
    }
  ];
  checkedItems = []; // 选中的测试项
  checkedItemsMap: any = {}; // 选中的测试项map，以测试项的key为键

  totoProgress = 0; // 总进度

  topic = 'testRunningData';
  subscription: Subscription; // 订阅实时测试数据的句柄

  logList: any = [];
  testItemsForTestAgent = [];
  private testItem: any;
  showLogView = true;
  device: any;
  reportId = null;
  deviceList = [];
  canDeactivate = true;
  testDesc = '';
  autoTest = false;

  constructor(private modalService: NzModalService, private http: HttpClient, private mqttService: MqttService, private appRef: ApplicationRef,
              private nzContextMenuService: NzContextMenuService) {
  }

  get agentUrl(): string {
    return environment.testAgentServerUrl.replace('${ip}', this.device.ip);
  }

  ngOnInit(): void {

    /*   if (!environment.production) {
         // 模拟下位机发送心跳
         setInterval(() => {
           this.http.post('online', {soleId: '999999999', hostName: 'DESKTOP-GU900F7', ip: '192.168.0.211'}).subscribe();
         }, 5000);
       }*/
    this.nodes.forEach(l1 => {
      l1.children.forEach(item => {
        item.testType = l1.key;
      });
    });
    console.log('开始订阅', this.topic);
    // 开始订阅服务端发送来的测试实时数据
    this.subscription = this.mqttService.observe(this.topic).subscribe(data => {
      const msg = data.payload.toString();
      const msgData = JSON.parse(msg);
      console.log('接收到下位机消息', msgData);
      const item = this.checkedItemsMap[msgData.key];
      if (!item.manual) {
        this.receiveRunningData(msgData);
      } else {
        // 丢弃下位机反馈的手动测试项的测试结果。
        console.log('丢弃下位机反馈的手动测试项的测试结果: ', msgData);

      }

    });
    // 获取所有在线下位机
    this.http.post('getAllTestAgent', {}).subscribe(({agentList: agentList}: any) => {
      (agentList as Array<any>).forEach(agent => {
        this.getTestAgentBaseInfo(agent.ip);
      });
    });
    // 接收下位机上线通知
    this.mqttService.observe('testAgentOnline').subscribe(data => {
      const msg = data.payload.toString();
      const msgData = JSON.parse(msg);
      const ip = msgData.ip;
      const key = msgData.key;
      const method = msgData.method;
      if (method === 'remove') {
        // 如果已经选择了被测设备，并且被测设备下线了，取消选择。
        if (this.device && this.device.key === key) {
          this.device = null;
        }
        this.deviceList.splice(this.deviceList.findIndex(value => value.key === key), 1);
      } else if (method === 'add') {
        this.getTestAgentBaseInfo(ip);
      } else {
        // 更新ip
        this.deviceList.splice(this.deviceList.findIndex(device => device.key === key), 1);
        this.getTestAgentBaseInfo(ip);
      }
    });
  }

  getTestAgentBaseInfo(ip): void {
    // 只接收未上线的下位机的上线信息。
    const url = environment.testAgentServerUrl.replace('${ip}', ip) + 'req/pc/basicinfo/computer';
    /*    if (!environment.production) {
          url = 'baseInfo';
        }*/
    this.http.post(url, {}).subscribe(baseInfo => {
      console.log('有新的下位机上线', baseInfo);
      this.deviceList.push({ip, ...baseInfo});
      if (!this.device) {
        this.device = this.deviceList[0];
        this.deviceChangeEvent.emit(this.device);
      }
    });
  }

  receiveRunningData(data: TestItem): void {
    if (this.testStatus !== 'running') {
      // 非测试执行中状态下，接收到测试进度数据，不予处理。
      return;
    }

    const item = this.checkedItemsMap[data.key];
    if (item) {
      if (this.totoProgress === 0) {
        this.addLog(`开始${item.title}。`);
      }
      item.progress = data.progress;
      const itemTotal = this.checkedItems.reduce((previousValue, currentValue) => previousValue + currentValue.progress, 0);
      this.totoProgress = Math.ceil(itemTotal / this.checkedItems.length);

      if (item.progress === 100) {
        item.result = data.result;
        let passStr = '';
        if (item.result.passed) {
          passStr = `测试结果：【${item.result.passed === 'Y' ? '通过' : '不通过'}】。`;
        }
        let info = '';
        if (item.result.info) {
          info = `详情：${item.result.info}。`;
        }
        this.addLog(`完成${item.title}。${passStr}${info}`);
        if (this.totoProgress !== 100) {
          const nextItem = this.checkedItems[this.checkedItems.indexOf(item) + 1];
          if (!nextItem) {
            console.error('testItem 不存在下一个测试项,但是未全局结束' + JSON.stringify(item));
          } else {
            this.addLog(`开始${nextItem.title}。`);
          }
        } else {
          this.addLog(`测试完成。`);

        }
        if (this.testItemsForTestAgent.length !== 0) {
          this.runNextTestItem();
        } else {
          // 所有测试项均测试完成。
          // 停止
          this.testStatus = 'finished';
          this.canDeactivate = true;
          MessageService.showGlobalMessage('success', '测试完成！');
          // TODO: 将测试记录数据保存到数据库中
          const testResultData = this.checkedItems.map(value => {
            return {title: value.title, key: value.key, result: value.result};
          });
          this.saveReportToDB();
          console.log(JSON.stringify(testResultData));
        }
      }
    }
  }

  ngOnDestroy(): void {
    // 页面销毁时，取消订阅实时数据
    this.subscription.unsubscribe();
    console.log('取消订阅', this.topic);
  }


  nzClick(event: NzFormatEmitEvent): void {
    console.log(event);
  }

  nzCheck(event: NzFormatEmitEvent): void {
    console.log(event);
    this.nodes.forEach(rootNode => {
      rootNode.show = false;
      rootNode.children.forEach(childNode => {
        if (childNode.checked) {
          rootNode.show = true;
        }
      });
    });
  }

  // 开始测试之前检查是否需求配置测试参数
  beforeTesting(): void {
    if (this.testStatus === 'running' || this.testStatus === 'suspend') {
      return;
    }
    if (!this.device) {
      MessageService.showGlobalMessage('error', '请选择被测设备');
      return;
    }
    const checkedMap = this.nzTreeComponent.getCheckedNodeList().reduce((previousValue, currentValue, currentIndex, array) => {
      previousValue[currentValue.key] = true;
      currentValue.children.forEach(child => {
        previousValue[child.key] = true;
      });
      return previousValue;
    }, {});
    // const showConfigView = ['ipConfig', 'RS422', 'RS232'].reduce((previous, current) => previous || checkedMap[current], false);
    if (this.nzTreeComponent.getCheckedNodeList().length === 0) {
      MessageService.showGlobalMessage('error', '请选择测试项');
    } else {
      // 需要配置界面
      // 在modal的open事件总监听子组件的表单change事件，从而控制ok按钮的状态
      const event = new EventEmitter();
      event.subscribe((valid) => {
        this.modalRef.getConfig().nzOkDisabled = !valid;
      });
      this.modalRef = this.modalService.create({
        nzContent: PropsConfigComponent,
        nzWidth: 900,
        nzComponentParams: {
          testItemMap: checkedMap,
          statusChange: event,
          device: this.device
        },
        nzOkDisabled: true,
        nzOnOk: () => {
          // 表单验证通过
          const parameterData = this.modalRef.componentInstance.validateForm.value;
          this.runTest(parameterData);
          return true;
        }
      });
    }
  }

  runTest(parameterData): void {
    this.testDesc = parameterData.desc;
    // 将测试状态改为进行中
    this.testStatus = 'running';
    this.canDeactivate = false;
    this.addLog('开始测试');
    // 找出选中的测试项
    this.checkedItems = [];
    this.checkedItemsMap = {};
    this.testItemsForTestAgent = [];
    this.reportId = null;
    this.nodes.forEach(rootNode => {
      rootNode.children.forEach(childNode => {
        if (childNode.checked) {
          // const item = Object.assign({}, childNode);
          const item = childNode;
          // 将测试想的测试进度设置为0，确保测试可以多次执行
          item.progress = 0;
          if (parameterData && parameterData[childNode.key]) {
            item.parameter = parameterData[childNode.key];
          }
          this.checkedItems.push(item);
          this.checkedItemsMap[item.key] = item;
          item.progress = 0;
          this.testItemsForTestAgent.push(item);
        }
      });
    });
    // this.testControl('running', this.checkedItems);
    // 在数据库中创建测试记录
    this.saveReportToDB();
    this.runNextTestItem();
  }

  private saveReportToDB(): void {
    console.log('save to DB');
    const submitData: any = {
      entityName: 'DmzReport',
      fieldMap: {
        json: JSON.stringify({baseInfo: this.device, testItems: this.checkedItems, temperatureAvgData: this.temperatureAvgData})
      },
      autoPK: true
    };
    if (!this.reportId) {
      // 新增数据
      submitData.fieldMap.desc = this.testDesc;
      submitData.fieldMap.device = this.device.basicInfo.hostName;
      submitData.fieldMap.testTime = UtilMisc.formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss');
      submitData.fieldMap.testUser = Auth.userData.userName;
    } else {
      submitData.fieldMap.reportId = this.reportId;
    }
    submitData.fieldMap.status = TestStatusMap[this.testStatus];
    console.log('genericSave');
    this.http.post('genericSave', submitData).toPromise().then(({value: value}: any) => {
      // FIXME: chrome的bug，这个数据会被保存两次，数据库会产生两条记录。
      if (!this.reportId) {
        console.log('create report.');
        this.reportId = value.reportId;
      }
      this.reportChangeEvent.emit(value);
    });
  }

  private runNextTestItem(): void {
    // 从待执行的测试项数组中取第一个测试项，并从数组中删除。
    this.testItem = this.testItemsForTestAgent.shift();
    const key = this.testItem.key;
    if (this.testItem.manual) {
      if (this.testItem.needTestAgent && this.testItem.key !== 'audioTest') {
        this.sendTestToTestAgent(this.testItem);
      }
      // 弹出测试结果确认界面
      const startEvent = new EventEmitter();
      startEvent.subscribe((valid) => {
        this.sendTestToTestAgent(this.testItem);
      });
      const cancelEvent = new EventEmitter();
      cancelEvent.subscribe((e) => {
        this.suspend();
        return true;
      });
      this.modalService.create({
        nzContent: ResultConfirmComponent,
        nzWidth: 400,
        nzComponentParams: {testItem: this.checkedItemsMap[key], startEvent},
        // nzOkDisabled: true,
        nzMaskClosable: false,
        nzOnCancel: (comp: ResultConfirmComponent) => {

          this.suspend();
          return true;
        },
        nzOnOk: (comp: ResultConfirmComponent) => {

          this.receiveRunningData({key, progress: 100, result: comp.result});
          return true;
        }
      });
    } else {
      this.sendTestToTestAgent(this.testItem);
    }
  }

  // 向下位机下发测试项。
  private sendTestToTestAgent(testItem): void {
    // 通知下位机开始测试
    this.http.post(this.agentUrl + 'req/test/begin', {
      testItems: [{
        testItem: testItem.key,
        parameter: testItem.parameter
      }]
    }).subscribe((value: any) => {
      if (value.result === -102) {
        MessageService.showGlobalMessage('error', '存在未完成的测试。');
      }
      console.log('value=', value);
    });
    // 模拟接收进度数据
    // 手动测试的测试项，不用等待下位机反馈测试结束信息，由上位机确认测试结束。
    if (!testItem.manual) {
      // 各测试项由预计测试时长，这里根据预估的时长跑进度，
      // 当进度跑到99%，还没有收到下位机测试完成的消息，则测试进度停在99%，等待下位机测试结束。
      let progress = 0;
      if (testItem.progressInterval) {
        clearInterval(testItem.progressInterval);
      }
      testItem.progressInterval = setInterval(() => {
        progress = progress + 100 / (testItem.timeCost / 5.0);

        const item = this.checkedItemsMap[testItem.key];
        if (item.progress === 100) {
          // 下位机已经测试完成，定时器可以取消了。
          clearInterval(testItem.progressInterval);
          return;
        }
        if (progress >= 90) {
          // 卡在99%，等待下位机测试结束。
          progress = 90;
          clearInterval(testItem.progressInterval);
        }

        // console.log(progress);
        this.receiveRunningData({key: testItem.key, progress});
      }, 5);
    }


  }

  private addLog(log: string): void {
    const date = UtilMisc.formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss');
    this.logList.push(`${date}: ${log}`);
    this.appRef.tick();
    try {
      this.logContent.nativeElement.scrollTop = this.logContent.nativeElement.scrollHeight;
    } catch (err) {
    }

  }

  // 暂停测试
  suspend(): void {
    if (this.testStatus === 'running') {
      clearInterval(this.testItem.progressInterval);
      this.testControl('suspend');
    }
  }

  // 继续测试
  continueRun(): void {
    if (this.testStatus === 'suspend') {
      this.testControl('running');
    }
  }

  // 停止测试
  stop(): void {
    if (this.testStatus === 'running' || this.testStatus === 'suspend') {
      this.testControl('stop');
      this.http.post(this.agentUrl + 'req/test/cancel', {}).subscribe((value: any) => {
        console.log('cancel result =', value);
        this.canDeactivate = true;
      });
    }
  }

  testControl(status: TestStatus, testData?: any): void {
    switch (status) {
      case 'suspend':
        this.addLog('暂停测试');
        break;
      case 'running':
        if (this.testStatus === 'suspend') {
          this.addLog('继续测试');
          // 继续测试时，从上次暂停的测试项开始测试（包含被暂停的测试项）
          this.testItemsForTestAgent.unshift(this.testItem);
          this.runNextTestItem();
        }
        break;
      case 'stop':
        this.addLog('停止测试');
        break;
    }
    this.testStatus = status;
    this.saveReportToDB();
    /*
        const submitData: any = {status};
        if (testData) {
          submitData.testData = testData.map(item => {
            const obj: any = {key: item.key, title: item.title};
            if (item.parameter) {
              obj.parameter = item.parameter;
            }
            return obj;
          });
        }
        this.http.post('testControl', submitData).subscribe(value => {
          // console.log('停止测试');
        });*/
  }

  contextMenu($event: MouseEvent, menu: NzDropdownMenuComponent): void {
    this.nzContextMenuService.create($event, menu);
  }

  clearLog(): void {
    this.logList.splice(0, this.logList.length);
  }

  // 生成报告
  viewReport(): void {
    if (this.reportId) {
      // window.open('reportInfo/' + this.reportId, '_blank', 'toolbar=yes,scrollbars=yes,resizable=yes');
      window.open('reportInfo/' + this.reportId);
    }
  }

  autoTestChange($event: Event): void {
    if ($event) {
      this.nodes.forEach(rootNode => {
        rootNode.show = true;
        rootNode.children.forEach(childNode => {
          childNode.checked = true;
          this.defaultCheckedKeys.push(childNode.key);
        });
      });
      this.defaultCheckedKeys = [...this.defaultCheckedKeys];
    } else {
      this.nodes.forEach(rootNode => {
        rootNode.show = false;
        rootNode.children.forEach(childNode => {
          childNode.checked = false;
        });
      });
      this.defaultCheckedKeys = [];
    }
  }
}
